/*
 * Copyright (c) 2021 Talkweb Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <stdio.h>
#include <unistd.h>

#include "ohos_init.h"
#include "cmsis_os2.h"
#include "iot_i2c.h"
#include "iot_errno.h"
#include "iot_gpio.h"
#include "iot_gpio_ex.h"

#define TASK_STACK_SIZE 4096
#define TASK_PRIO 25

#define IOT_I2C_IDX_0 0
#define SHT30_ADDR     0x44 //0x44接地，0x45接电源
#define WriteHeader    (SHT30_ADDR<<1)       //地址+写命令
#define ReadHeader     (WriteHeader|0X01)    //地址+读命令

#define  CMD_READ_SERIALNBR  0x3780  // read serial number
#define  CMD_MEAS_CLOCKSTR_H 0x2C06  // meas. clock stretching, high rep.

// CRC
#define POLYNOMIAL  0x131 // P(x) = x^8 + x^5 + x^4 + 1 = 100110001
static unsigned char SHT3XCalcCrc(unsigned char data[], unsigned char nbrOfBytes)
{
    unsigned char bit = 0;        // bit mask
    unsigned char crc = 0xFF; // calculated checksum
    unsigned char byteCtr = 0;    // byte counter

    // calculates 8-Bit checksum with given polynomial
    for(byteCtr = 0; byteCtr < nbrOfBytes; byteCtr++)
    {
        crc ^= (data[byteCtr]);
        for(bit = 8; bit > 0; --bit)
        {
            if(crc & 0x80)
            {
                crc = (crc << 1) ^ POLYNOMIAL;
            } 
            else           
            {
                crc = (crc << 1);
            }
        }
    }

    return crc;
}

//温度转换
float SHT3XCalcTemperature(unsigned char *buf)
{
    unsigned short rawValue = 0;

    rawValue = (((unsigned int)buf[0]) << 8) | ((unsigned int)buf[1]);
    return 175 * (float)rawValue / 65535 - 45;
}

//湿度转换
float SHT3XCalcHumidity(unsigned char *buf)
{
    unsigned short rawValue = 0;

    rawValue = (((unsigned short)buf[0]) << 8) | ((unsigned short)buf[1]);
    return 100 * (float)rawValue / 65535;
}

//SHT3X周期性模式读取数据函数,一次读取4byte
static int  SHT3xPeriodicReadData(unsigned short cmd, unsigned char *pbuf)
{
    int ret = 0;	
    unsigned char temp_crc = 0, hum_crc = 0;
    unsigned char buf[6] = {0};

    buf[0] = (unsigned char)(cmd >> 8)&0XFF;
    buf[1] = (unsigned char)(cmd >> 0)&0XFF;
    ret = IoTI2cWrite(IOT_I2C_IDX_0, WriteHeader, buf, 2);
    if(ret != IOT_SUCCESS)
    {
        printf("I2C write data fail, ret = %d\r\n",ret);
        return ret;
    }

    usleep(20000);  
    ret = IoTI2cRead(IOT_I2C_IDX_0, ReadHeader, buf, 6);
    if(ret != IOT_SUCCESS)
    {
        printf("I2C read data fail, ret = %d\r\n",ret);
        return ret;
    }

    //crc校验读取到的数据
    temp_crc = buf[2];
    hum_crc = buf[5];
    if((temp_crc != SHT3XCalcCrc(buf,2)) || (hum_crc != SHT3XCalcCrc(&buf[3],2)))
    {
        printf("I2C read data crc check fail\r\n");
        return IOT_FAILURE;								
    }   

    pbuf[0] = buf[0];
    pbuf[1] = buf[1];
    pbuf[2] = buf[3];
    pbuf[3] = buf[4];   

    return IOT_SUCCESS;
}

//读取芯片序列号
static void SHT3xReadSerialNumber(unsigned int* serialNbr)
{ 
    unsigned char number_buf[4] = {0};

    SHT3xPeriodicReadData(CMD_READ_SERIALNBR,number_buf);	
    *serialNbr = (((unsigned int)number_buf[0]) << 24) + (((unsigned int)number_buf[1]) << 16) +
    (((unsigned int)number_buf[2]) << 8) + ((unsigned int)number_buf[3]);	
}

static void I2cSht30Task(void)
{	
    int ret = 0;
    unsigned int serialNumber = 0; 
    unsigned char read_buf[4] = {0};
    float temp = 0, hum = 0;

    IoTGpioInit(IOT_IO_NAME_GPIO_13);
    IoTGpioInit(IOT_IO_NAME_GPIO_14);

    IoSetPull(IOT_IO_NAME_GPIO_13, IOT_IO_PULL_UP);
    IoSetPull(IOT_IO_NAME_GPIO_14, IOT_IO_PULL_UP);

    IoSetFunc(IOT_IO_NAME_GPIO_13, IOT_IO_FUNC_GPIO_13_I2C0_SDA);	
    IoSetFunc(IOT_IO_NAME_GPIO_14, IOT_IO_FUNC_GPIO_14_I2C0_SCL);	

    ret = IoTI2cInit(IOT_I2C_IDX_0, 400000);
    if(ret != IOT_SUCCESS)
    {
        printf("I2c Init failed, ret = %d\r\n",ret);
    }

    SHT3xReadSerialNumber(&serialNumber);
    printf("sht30 serialNumber = %08X\r\n", serialNumber);

    for(;;)
    {
        if(SHT3xPeriodicReadData(CMD_MEAS_CLOCKSTR_H,read_buf) == IOT_SUCCESS)
        {
            temp = SHT3XCalcTemperature(&read_buf[0]);
            hum  = SHT3XCalcHumidity(&read_buf[2]);
        }  

        printf("sht30->> temp = %.02f, hum = %.01f\r\n",temp, hum);
        usleep(2000000);
    }
}

static void I2cSht30Example(void)
{
    osThreadAttr_t attr;
    attr.name = "I2cSht30Task";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = TASK_STACK_SIZE;
    attr.priority = TASK_PRIO;

    if (osThreadNew((osThreadFunc_t)I2cSht30Task, NULL, &attr) == NULL) 
    {
        printf("[thinkgo] Falied to create I2cSht30Task!\n");
    }
    return;
}

APP_FEATURE_INIT(I2cSht30Example);
